use core::ptr::copy_nonoverlapping;

use utils::ufmt;

use crate::export::device;

pub fn init() {
    let rcc = unsafe { device::RCC::steal() };
    rcc.ahb2enr().modify(|_, w| w.rngen().enabled());

    let rng = unsafe { device::RNG::steal() };
    rng.cr().modify(|_, w| w.rngen().enabled());
}

#[derive(Debug, ufmt::derive::uDebug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
    SEIS,
    CEIS,
}

pub fn random_u32() -> Result<u32, Error> {
    let rng = unsafe { device::RNG::steal() };
    loop {
        let sr = rng.sr().read();
        if sr.seis().is_fault() {
            return Err(Error::SEIS);
        }
        if sr.ceis().is_slow() {
            return Err(Error::CEIS);
        }
        if sr.drdy().is_valid() {
            break;
        }
    }
    Ok(rng.dr().read().rndata().bits())
}

pub fn random_bytes(buf: &mut [u8]) -> Result<(), Error> {
    let len = buf.len();
    let ptr = buf.as_mut_ptr();
    let mut index = 0;

    while index < len {
        let value = random_u32()?;
        let n = len - index;
        if n >= 4 {
            unsafe {
                let bytes = u32::to_ne_bytes(value);
                copy_nonoverlapping(bytes.as_ptr(), ptr.add(index), 4);
            }
            index += 4;
        } else if n >= 2 {
            unsafe {
                let bytes = u16::to_ne_bytes(value as u16);
                copy_nonoverlapping(bytes.as_ptr(), ptr.add(index), 2);
            }
            index += 2;
        } else if n >= 1 {
            unsafe {
                let bytes = u8::to_ne_bytes(value as u8);
                copy_nonoverlapping(bytes.as_ptr(), ptr.add(index), 1);
            }
            index += 1;
        }
    }

    Ok(())
}
